﻿using System;
using System.Threading;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Diagnostics;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using KeyEventArgs = xEngine.Input.KeyEventArgs;

using xEngine.Debugging;
using xEngine.Rendering;
using xEngine.Input;
using xEngine.Utils;

namespace xEngine.UI
{
    abstract public class XControl : Control, XRenderer, IGraphicsDeviceService
    {
        #region Member Variables

        private InputManager _input;
        private Stopwatch _timer;
        private TimeSpan _lastTime;
        private PresentationParameters _parameters;
        private bool _disposing;

        #endregion

        #region Properties

        public GraphicsDevice GraphicsDevice { get; private set; }
        public GameServiceContainer Services { get; private set; }
        public SpriteBatch SpriteBatch { get; private set; }
        public ContentManager Content { get; private set; }

        public bool Initialized { get; protected set; }
        public bool ShowConsole { get; set; }
        public bool ShowOSD { get; set; }

        protected InputManager Input { get { return _input; } }

        #endregion

        #region Constructors

        public XControl()
        {
            Services = new GameServiceContainer();
            RenderManager.Initialize(this);
            DataLoader.Initialize(this);
            
            _lastTime = new TimeSpan();

            SetStyle(ControlStyles.Selectable, true);

            new Thread(new ThreadStart(delegate
            {
                while (!Disposing && !_disposing && !IsDisposed)
                {
                    Repaint();

                    if (_timer != null)
                    {
                        int wait = (int)(16.6 - (_timer.Elapsed - _lastTime).TotalMilliseconds);
                        if (wait > 0)
                            Thread.Sleep(wait);
                    }
                    else
                        Thread.Sleep(500);
                }
            })).Start();
        }

        #endregion

        #region Functions

        delegate void RepaintDelegate();
        private void Repaint()
        {
            if (InvokeRequired && !IsDisposed)
                try { Invoke(new RepaintDelegate(Repaint)); } catch { }
            else
                OnPaint(null);
        }

        private bool HandleDeviceReset()
        {
            switch (GraphicsDevice.GraphicsDeviceStatus)
            {
                case GraphicsDeviceStatus.Lost:
                    return false;

                case GraphicsDeviceStatus.NotReset:
                    if (DeviceResetting != null)
                        DeviceResetting(this, EventArgs.Empty);

                    _parameters.BackBufferWidth = Math.Max(ClientSize.Width, 1);
                    _parameters.BackBufferHeight = Math.Max(ClientSize.Height, 1);

                    GraphicsDevice.Reset(_parameters);

                    XConsole.VideoSettingsChanged();

                    if (DeviceReset != null)
                        DeviceReset(this, EventArgs.Empty);
                    return true;
            }

            return true;
        }

        protected override void OnResize(EventArgs e)
        {
            if (GraphicsDevice != null)
            {
                Viewport v = new Viewport(0, 0, ClientSize.Width, ClientSize.Height);
                v.MinDepth = 0;
                v.MaxDepth = 1;
                if (!_disposing)
                    GraphicsDevice.Viewport = v;

                SpriteBatch = new SpriteBatch(GraphicsDevice);
                XConsole.VideoSettingsChanged();
            }

            //base.OnResize(e);
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            if (DesignMode)
            {
                base.OnPaint(e);
                return;
            }
            
            if (GraphicsDevice == null || !HandleDeviceReset())
                return;

            TimeSpan delta = _timer.Elapsed - _lastTime;
            _lastTime = _timer.Elapsed;
            GameTime time = new GameTime(_lastTime, delta);

            if(!_disposing)
                _input.ProcessMouse();
    
            if (!_disposing)            
                _input.ProcessKeyboard();

            if (!_disposing)
                Update(time);

            if (!_disposing)
                GraphicsDevice.Clear(Color.Black);

            if (!_disposing)
                Render(time);

            if (!_disposing && ShowOSD)
                OSD.Render();

            if (!_disposing && ShowConsole)
                XConsole.Render();
            
            if(!_disposing)
                GraphicsDevice.Present(new Rectangle(0, 0, ClientSize.Width, ClientSize.Height), null, this.Handle);
        }
        protected override void OnCreateControl()
        {
            if (!DesignMode)
            {
                _parameters = new PresentationParameters();
                _parameters.BackBufferWidth = Math.Max(ClientSize.Width, 1);
                _parameters.BackBufferHeight = Math.Max(ClientSize.Height, 1);
                _parameters.BackBufferFormat = SurfaceFormat.Color;
                _parameters.DepthStencilFormat = DepthFormat.Depth24;
                _parameters.DeviceWindowHandle = Handle;
                _parameters.PresentationInterval = PresentInterval.Immediate;
                _parameters.IsFullScreen = false;
                _parameters.RenderTargetUsage = RenderTargetUsage.PreserveContents;

                GraphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.HiDef, _parameters);
                Services.AddService(typeof(IGraphicsDeviceService), this);
                Content = new XContentManager(Services);

                _input = new InputManager(new Point(_parameters.BackBufferWidth / 2, _parameters.BackBufferHeight / 2));
                _input.MouseMoved += new EventHandler<MouseMovedEventArgs>(delegate(object s, MouseMovedEventArgs e) {
                    e.ResetToCenter = false;
                    if(ClientRectangle.Contains(PointToClient(new System.Drawing.Point(e.Position.X, e.Position.Y))))
                        OnMouseMoved(e);
                });
                _input.MouseDown += new EventHandler<MouseButtonEventArgs>(delegate(object s, MouseButtonEventArgs e) {
                    if (ContainsFocus && ClientRectangle.Contains(PointToClient(new System.Drawing.Point(e.Position.X, e.Position.Y))))
                        OnMouseDown(e);
                });
                _input.MouseUp += new EventHandler<MouseButtonEventArgs>(delegate(object s, MouseButtonEventArgs e) {
                    if (ClientRectangle.Contains(PointToClient(new System.Drawing.Point(e.Position.X, e.Position.Y)))) 
                        OnMouseUp(e);
                });
                _input.KeyDown += new EventHandler<KeyEventArgs>(OnKeyDown);
                _input.KeyUp += new EventHandler<KeyEventArgs>(OnKeyUp);

                Control c = this;
                while (!typeof(Form).IsInstanceOfType(c))
                    c = c.Parent;

                c.MouseWheel += new MouseEventHandler(delegate(object s, System.Windows.Forms.MouseEventArgs e) {
                    if (ClientRectangle.Contains(PointToClient(e.Location))) 
                        OnMouseWheel(e);
                });

                SpriteBatch = new SpriteBatch(GraphicsDevice);

                GenericResources.Initialize(GraphicsDevice);
                XConsole.Font = GenericResources.Fonts.Fixed08;

                XConsole.VideoSettingsChanged();

                _timer = Stopwatch.StartNew();
                
                Initialize();

                Initialized = true;
            }

            base.OnCreateControl();
        }
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    if (DeviceDisposing != null)
                        DeviceDisposing(this, EventArgs.Empty);

                    _disposing = true;

                    GraphicsDevice.Dispose();
                }

                GraphicsDevice = null;

                if (_timer != null)
                    _timer.Stop();
            }
            catch (System.Exception e)
            {
                LogWriter.WriteLine = e;
            }

            base.Dispose(disposing);
        }

        protected virtual void Initialize() { }
        protected virtual void Update(GameTime time) { }
        protected abstract void Render(GameTime time);

        public virtual bool ProcessConsoleCommand(string cmd, params string[] param) { return false; }

        #endregion

        #region Events

        public event EventHandler<EventArgs> DeviceCreated;
        public event EventHandler<EventArgs> DeviceDisposing;
        public event EventHandler<EventArgs> DeviceReset;
        public event EventHandler<EventArgs> DeviceResetting;

        protected virtual void OnMouseDown(MouseButtonEventArgs e) { }
        protected virtual void OnMouseUp(MouseButtonEventArgs e) { }
        protected virtual void OnMouseMoved(MouseMovedEventArgs e) { }
        protected virtual void OnMouseWheel(System.Windows.Forms.MouseEventArgs e) { }
        protected virtual void OnKeyDown(object s, KeyEventArgs e) { }
        protected virtual void OnKeyUp(object s, KeyEventArgs e) { }

        #endregion
    }
}
